﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Support;
//using Asol.Reporting.Editor.Editor;

namespace Asol.Reporting.Editor.Data
{
    #region class ReportGrid : data o nastavení mřížky reportu (přichytávací tečky)
    /// <summary>
	/// ReportGrid : data o nastavení mřížky reportu (přichytávací tečky)
	/// </summary>
	public class ReportGrid
	{
		#region Konstrukce a public property
		/// <summary>
		/// ReportGrid : data o nastavení Gridu.
		/// Konstruktor.
		/// </summary>
		public ReportGrid()
		{
			this.SpacingHorizontal = 5f;
			this.SpacingVertical = 5f;
			this.ShowGrid = ShowGridType.None;
			this.SnapToGrid = true;
			this.SnapToObjects = true;
		}
		/// <summary>
		/// Rozteč bodů mřížky ve směru X, v milimetrech
		/// </summary>
		[Category("Layout")]
		[DisplayName("Rozteč X")]
		[Description("Rozteč bodů mřížky ve směru X, v milimetrech")]
		public float SpacingHorizontal { get; set; }
		/// <summary>
		/// Rozteč bodů mřížky ve směru Y, v milimetrech
		/// </summary>
		[Category("Layout")]
		[DisplayName("Rozteč Y")]
		[Description("Rozteč bodů mřížky ve směru Y, v milimetrech")]
		public float SpacingVertical { get; set; }
		/// <summary>
		/// Režim zobrazování Gridu
		/// </summary>
		[Category("Layout")]
		[DisplayName("Druh mřížky")]
		[Description("Druh zobrazování mřížky")]
		public ShowGridType ShowGrid { get; set; }
		/// <summary>
		/// Přichytávat na Grid
		/// </summary>
		[Category("Behaviour")]
		[DisplayName("Přichytávat na mřížku")]
		[Description("Při posouvání prvků přichytávat k nejbližšímu bodu mřížky")]
        [Editor(typeof(CheckBoxEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public bool SnapToGrid { get; set; }
		/// <summary>
		/// Přichytávat na okolní objekty
		/// </summary>
		[Category("Behaviour")]
		[DisplayName("Přichytávat na objekty")]
		[Description("Při posouvání prvků přichytávat k odpovídajícímu sousednímu objektu")]
        [Editor(typeof(CheckBoxEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public bool SnapToObjects { get; set; }
		#endregion
        #region Tvorba dat pro vizuální Grid
        /// <summary>
        /// Vytvoří souřadnice gridu X i Y.
        /// </summary>
        /// <param name="args">Kreslící data</param>
        /// <param name="visualBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech). Na souřadnici (Location) je bod 0/0 tohoto bandu.</param>
        /// <param name="visibleArea">Viditelná část prostoru. Koordináty jsou shodné jako v visualBound, toto je viditelná podmnožina.</param>
        /// <param name="gridXCoords">Výstup souřadnic X</param>
        /// <param name="gridYCoords">Výstup souřadnic Y</param>
        internal void GridCreateCoordinates(EditPaintEventArgs args, RectangleF visualBound, RectangleF visibleArea, out int[] gridXCoords, out int[] gridYCoords)
        {
            gridXCoords = this.CreateCoordinatesX(args, visualBound, visibleArea);
            gridYCoords = this.CreateCoordinatesY(args, visualBound, visibleArea);
        }
        /// <summary>
        /// Vytvoří stringový otisk dat, která určují souřadnice gridu pro dané parametry
        /// </summary>
        /// <param name="visualBound"></param>
        /// <param name="visibleArea"></param>
        /// <param name="zoom"></param>
        /// <returns></returns>
        internal string GridGetCoordMark(RectangleF visualBound, RectangleF visibleArea, float zoom)
        {
            string floatFmt = "######0.00";
            return
                Convertor.RectangleFToString(visualBound) + "; " +
                Convertor.RectangleFToString(visibleArea) + "; " +
                zoom.ToString(floatFmt) + "; " +
                this.SpacingHorizontal.ToString(floatFmt) + "; " +
                this.SpacingVertical.ToString(floatFmt) + "; " +
                this.ShowGrid.ToString();
        }
        /// <summary>
        /// Vrací seznam pozic gridu pro daný prostor pro směr X
        /// </summary>
        /// <param name="args">Kreslící data</param>
        /// <param name="visualBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech). Na souřadnici (Location) je bod 0/0 tohoto bandu.</param>
        /// <param name="visibleArea">Viditelná část prostoru. Koordináty jsou shodné jako v visualBound, toto je viditelná podmnožina.</param>
        /// <returns></returns>
        public int[] CreateCoordinatesX(EditPaintEventArgs args, RectangleF visualBound, RectangleF visibleArea)
        {
            double spacing = ((double)this.SpacingHorizontal * args.ViewPortConvertor.VisualFromLogical(1000f)) / 1000d;
            return CreateCoordinates(visualBound.X, visualBound.Width, visibleArea.X, visibleArea.Width, spacing);
        }
        /// <summary>
        /// Vrací seznam pozic gridu pro daný prostor pro směr Y
        /// </summary>
        /// <param name="args">Kreslící data</param>
        /// <param name="visualBound">Prostor bandu, vizuální, bez omezení (celý band v pixelech). Na souřadnici (Location) je bod 0/0 tohoto bandu.</param>
        /// <param name="visibleArea">Viditelná část prostoru. Koordináty jsou shodné jako v visualBound, toto je viditelná podmnožina.</param>
        /// <returns></returns>
        public int[] CreateCoordinatesY(EditPaintEventArgs args, RectangleF visualBound, RectangleF visibleArea)
        {
            double spacing = ((double)this.SpacingVertical * args.ViewPortConvertor.VisualFromLogical(1000f)) / 1000d;
            return CreateCoordinates(visualBound.Y, visualBound.Height, visibleArea.Y, visibleArea.Height, spacing);
        }
        /// <summary>
        /// Vrátí vizuální souřadnice pozic gridu v jednom rozměru (tato metoda je na rozměru nezávislá).
        /// Vstupem jsou vizuální souřadnice celého prostoru, vizuální souřadnice viditelné části, a vzdálenost 1000 značek.
        /// </summary>
        /// <param name="areaBegin"></param>
        /// <param name="areaLength"></param>
        /// <param name="visibleBegin"></param>
        /// <param name="visibleLength"></param>
        /// <param name="spacing">Vyjadřuje vizuální vzdálenost (v pixelech) dvou sousedních značek.</param>
        /// <returns></returns>
        protected int[] CreateCoordinates(float areaBegin, float areaLength, float visibleBegin, float visibleLength, Double spacing)
        {
            if (spacing <= 0d) return new int[0];                  // Nezadaný spacing = žádné značky.
            while (spacing < 12d) spacing *= 2d;                   // Dokud je spacing menší než 12 pixelů, zdvojnásobuji jej.

            List<int> result = new List<int>();
            Double position = (Double)areaBegin;
            Double end = (Double)(areaBegin + areaLength);
            float visibleEnd = visibleBegin + visibleLength;
            while (position <= end)
            {
                float pos = (float)position;
                if (pos >= visibleBegin)
                {
                    if (pos > visibleEnd) break;
                    result.Add((int)(Math.Round(pos,0)));
                }
                position += spacing;
            }
            return result.ToArray();
        }
        #endregion
        #region Serializace
        /// <summary>
		/// Zajistí serializaci všech svých hodnot do předaného XML elementu.
		/// Element vytváří nadřízená vrstva (ReportDocument), úkolem komponenty je uložit do XML elementu všechny své hodnoty.
		/// Každá třída ukládá své hodnoty, před tím volá base.XmlSerializeTo(node), aby předkové uložili svoje data.
		/// </summary>
		/// <param name="node"></param>
		public virtual void XmlSerializeTo(XElement node)
		{
			node.SetAttributeValue("SpacingHorizontal", Convertor.SingleToString(this.SpacingHorizontal));
			node.SetAttributeValue("SpacingVertical", Convertor.SingleToString(this.SpacingVertical));
			node.SetAttributeValue("ShowGrid", Convertor.EnumToString(this.ShowGrid));
			node.SetAttributeValue("SnapToGrid", Convertor.BooleanToString(this.SnapToGrid));
			node.SetAttributeValue("SnapToObjects", Convertor.BooleanToString(this.SnapToObjects));
		}
		/// <summary>
		/// Načte do sebe veškerá data z XML nodu.
		/// Volitelně přepíše i svoje vlastní ID (pokud je overrideId = true). To se používá při čtení z XML dokumentu (Load, Revert, Undo, Redo).
		/// V případě Cut/Copy + Paste se overrideId předává false = vzniká nový téměř identický objekt k objektu původnímu, ale s novým ID.
		/// </summary>
		/// <param name="node">XML element</param>
		/// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
		public virtual void XmlFillFrom(XElement node, bool overrideId)
		{
			this.SpacingHorizontal = (Single)Convertor.StringToSingle(ReportDocument.XmlGetXAttributeValue(node, "SpacingHorizontal"));
			this.SpacingVertical = (Single)Convertor.StringToSingle(ReportDocument.XmlGetXAttributeValue(node, "SpacingVertical"));
			this.ShowGrid = (ShowGridType)Convertor.StringToEnum(ReportDocument.XmlGetXAttributeValue(node, "ShowGrid"), ShowGridType.None);
			this.SnapToGrid = (Boolean)Convertor.StringToBoolean(ReportDocument.XmlGetXAttributeValue(node, "SnapToGrid"));
			this.SnapToObjects = (Boolean)Convertor.StringToBoolean(ReportDocument.XmlGetXAttributeValue(node, "SnapToObjects"));
		}
		/// <summary>
		/// Statický konstruktor konkrétní instance, která je popsána v daném XML elementu.
		/// Jde o object factory metodu, vrací konkrétního potomka (podle hodnoty atributu Type).
		/// Pokud nelze objekt vytvořit, vrací null.
		/// </summary>
		/// <param name="node">XML element</param>
		/// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
		/// <returns></returns>
		public static ReportGrid XmlDeserializeFrom(XElement node, bool overrideId)
		{
			if (node == null) return null;

			ReportGrid target = new ReportGrid();
			target.XmlFillFrom(node, overrideId);
			return target;
		}
		#endregion
    }
	/// <summary>
	/// Druh zobrazení Gridu
	/// </summary>
	public enum ShowGridType
	{
		/// <summary>Nezobrazovat</summary>
		None,
		/// <summary>Zobrazit jako body</summary>
		ShowAsDots,
		/// <summary>Zobrazit jako křížky</summary>
		ShowAsCross,
		/// <summary>Zobrazit jako linky</summary>
		ShowAsLines
	}
	#endregion
}
